package cn.talk.doodledemo.utils.log;

import cn.talk.doodledemo.utils.log.interfaces.Printer;
import cn.talk.doodledemo.utils.log.utils.ObjectUtil;
import cn.talk.doodledemo.utils.log.utils.Utils;
import ohos.utils.zson.ZSONArray;
import ohos.utils.zson.ZSONException;
import ohos.utils.zson.ZSONObject;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.Date;
import java.util.HashMap;

import static com.zhihu.matisse.utils.TextUtils.isEmpty;

public abstract class AbstractPrinter implements Printer {

    private LogConfigImpl mLogConfig = LogConfigImpl.getInstance();
    ThreadLocal<String> localTags = new ThreadLocal<>();
    // 换行符
    String LINE_SEPARATOR = System.getProperty("line.separator");

    // 解析 StackTrace 跳过系统层级
    private static final int MIN_STACK_OFFSET = 5;

    private synchronized void logString(int type, String msg, Object... args) {
        logString(type, msg, null, false, args);
    }

    private void logObject(int type, Object object) {
        logString(type, ObjectUtil.objectToString(object));
    }

    private void logString(int type, String msg, String tag, boolean isPart, Object... args) {
        if (!isPart || isEmpty(tag)) {
            tag = generateTag();
        }
        if (!isPart) {
            if (args != null && args.length > 0) {
                try {
                    msg = String.format(msg, args);
                } catch (Exception e) {
                    printLog(LogLevel.TYPE_ERROR, tag, e.getLocalizedMessage());
                }
            }
        }

        // 不启用日志
        if (!mLogConfig.isEnable() || type < mLogConfig.getLogLevel()) {
            return;
        }

        // 超过长度分条打印
        if (msg.length() > ObjectUtil.LINE_MAX) {
            if (mLogConfig.isShowBorder()) {
                printLog(type, tag, Utils.printDividingLine(Utils.DIVIDER_TOP));
                printLog(type, tag, Utils.printDividingLine(Utils.DIVIDER_NORMAL) + getTopStackInfo());
                //printLog(type, tag, Utils.printDividingLine(Utils.DIVIDER_CENTER));
            }
            for (String subMsg : ObjectUtil.largeStringToList(msg)) {
                logString(type, subMsg, tag, true, args);
            }
            if (mLogConfig.isShowBorder()) {
                printLog(type, tag, Utils.printDividingLine(Utils.DIVIDER_BOTTOM));
            }
            return;
        }
        // 分条打印日志
        if (mLogConfig.isShowBorder()) {
            if (isPart) {
                for (String sub : msg.split(LINE_SEPARATOR)) {
                    printLog(type, tag, sub);
                }
            } else {
                printLog(type, tag, Utils.printDividingLine(Utils.DIVIDER_TOP));
                printLog(type, tag, Utils.printDividingLine(Utils.DIVIDER_NORMAL) + getTopStackInfo());
                for (String sub : msg.split(LINE_SEPARATOR)) {
                    printLog(type, tag, sub);
                }
                printLog(type, tag, Utils.printDividingLine(Utils.DIVIDER_BOTTOM));
            }
        } else {
            printLog(type, tag, msg);
        }
    }


    /**
     * 该日志等级能否存储到数据库
     *
     * @param type
     * @return
     */
    private boolean canStoreToDb(int type) {
        return type >= mLogConfig.getLogDbLevel();
    }

    private int getStackOffset(StackTraceElement[] trace, Class cla) {
        for (int i = MIN_STACK_OFFSET; i < trace.length; i++) {
            StackTraceElement e = trace[i];
            String name = e.getClassName();
            if (name.equals(cla.getName())) {
                return ++i;
            }
        }
        return -1;
    }

    /**
     * 获取当前activity栈信息
     *
     * @return StackTraceElement
     */
    private StackTraceElement getCurrentStackTrace() {
        StackTraceElement[] trace = Thread.currentThread().getStackTrace();
        int stackOffset = getStackOffset(trace, LogUtils.class);
        if (stackOffset == -1) {
            return null;
        }

        return trace[stackOffset];
    }

    /**
     * 获取最顶部stack信息
     *
     * @return String
     */
    protected String getTopStackInfo() {
        StackTraceElement caller = getCurrentStackTrace();
        if (caller == null) {
            return "";
        }

        String stackTrace = caller.toString();
        stackTrace = stackTrace.substring(stackTrace.lastIndexOf('('), stackTrace.length());
        String tag = "%s.%s%s";
        String callerClazzName = caller.getClassName();
        callerClazzName = callerClazzName.substring(callerClazzName.lastIndexOf(".") + 1);
        tag = String.format(tag, callerClazzName, caller.getMethodName(), stackTrace);
        return tag;
    }

    private String generateTag() {
        String tag = localTags.get();
        if (!StringUtils.isEmpty(tag)) {
            localTags.remove();
            return tag;
        }
        return mLogConfig.getTagPrefix();
    }


    public abstract void printLog(int type, String tag, String msg);

    @Override
    public void d(String message, Object... args) {
        logString(LogLevel.TYPE_DEBUG, message, args);
    }

    @Override
    public void d(Object object) {
        logObject(LogLevel.TYPE_DEBUG, object);
    }

    @Override
    public void e(String message, Object... args) {
        logString(LogLevel.TYPE_ERROR, message, args);
    }

    @Override
    public void e(Object object) {
        logObject(LogLevel.TYPE_ERROR, object);
    }

    @Override
    public void w(String message, Object... args) {
        logString(LogLevel.TYPE_WARM, message, args);
    }

    @Override
    public void w(Object object) {
        logObject(LogLevel.TYPE_WARM, object);
    }

    @Override
    public void i(String message, Object... args) {
        logString(LogLevel.TYPE_INFO, message, args);
    }

    @Override
    public void i(Object object) {
        logObject(LogLevel.TYPE_INFO, object);
    }

    @Override
    public void v(String message, Object... args) {
        logString(LogLevel.TYPE_VERBOSE, message, args);
    }

    @Override
    public void v(Object object) {
        logObject(LogLevel.TYPE_VERBOSE, object);
    }

    @Override
    public void json(String json) {
        if (StringUtils.isEmpty(json)) {
            return;
        }

        try {
            if (json.startsWith("{")) {
                HashMap<Object, Object> map = new HashMap<>();
                ZSONObject jsonObject = ZSONObject.stringToZSON(json);
                String msg = jsonObject.toString();
                i(msg);
            } else if (json.startsWith("[")) {
                ZSONArray jsonArray = ZSONArray.stringToZSONArray(json);
                String msg = jsonArray.toString();
                i(msg);
            }
        } catch (ZSONException e) {
            e(e.toString() + "\njson = " + json);
        }
    }

    public Printer setTag(String tag) {
        if (!StringUtils.isEmpty(tag)) {
            localTags.set(tag);
        }

        return this;
    }
}
